TODO

passar tabelas pra csv(todas?), rename blank hashtag/media, juntar valores de reply pra cluster? par(mfrow=c(1,2))

For R beginners

New chunk Ctrl+Alt+I

Execute chunk Ctrl+Shift+Enter

Execute all chunks Ctrl+Alt+R

HTML preview Ctrl+Shift+K

Library preparations

library(readr)

Attaching package: ‘readr’

The following object is masked from ‘package:koRpus’:

    tokenize
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyverse)
── Attaching core tidyverse packages ───────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ ggplot2::annotate() masks NLP::annotate()
✖ dplyr::filter()     masks stats::filter()
✖ dplyr::lag()        masks stats::lag()
✖ readr::tokenize()   masks koRpus::tokenize()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggplot2)
library(reshape2)

Attaching package: ‘reshape2’

The following object is masked from ‘package:tidyr’:

    smiths
library(stats)

Data Import

data <- read.csv("~/4year/2semester/dtII/CSVs/HEIs.csv",
                 colClasses = c(tweet_id = "character"))

# Modifying created_at type so that attribute can be used more easily 
data$created_at <- as.POSIXct(data$created_at,
                              format= "%Y-%m-%dT%H:%M:%S", tz="UTC")

#View(data)
summary(data)
      id              tweet_id             text               type           bookmark_count    favorite_count     retweet_count      reply_count      
 Length:11728       Length:11728       Length:11728       Length:11728       Min.   :  0.000   Min.   :    0.00   Min.   :   0.00   Min.   :   0.000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  0.000   1st Qu.:    7.00   1st Qu.:   2.00   1st Qu.:   0.000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  0.000   Median :   20.00   Median :   5.00   Median :   1.000  
                                                                             Mean   :  1.543   Mean   :   60.67   Mean   :  10.62   Mean   :   3.888  
                                                                             3rd Qu.:  1.000   3rd Qu.:   57.00   3rd Qu.:  11.00   3rd Qu.:   3.000  
                                                                             Max.   :418.000   Max.   :41655.00   Max.   :4214.00   Max.   :2317.000  
                                                                                                                                                      
   view_count        created_at                       hashtags             urls            media_type         media_urls       
 Min.   :      5   Min.   :2022-08-01 03:05:11.00   Length:11728       Length:11728       Length:11728       Length:11728      
 1st Qu.:   2643   1st Qu.:2022-10-19 12:56:27.00   Class :character   Class :character   Class :character   Class :character  
 Median :   6240   Median :2023-01-29 08:26:30.00   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :  14182   Mean   :2023-01-30 07:39:34.96                                                                              
 3rd Qu.:  16058   3rd Qu.:2023-05-05 14:16:43.25                                                                              
 Max.   :7604544   Max.   :2023-08-31 20:50:01.00                                                                              
 NA's   :4840                                                                                                                  

Initial Data Preparation

# Count of how many entries each HEI has
number_interactions <- data %>%
              group_by(id) %>% summarise(count = n())

number_interactions

Since complutense only has 1 entry we can’t learn anything from it, so we removed it

data <- data[data$id != "complutense.csv", ]

Visualization of number all posts, just tweets and just replies

number_posts <- data %>%
              group_by(id) %>% summarise(count = n())

number_tweets <- data[data$type == "Tweet", ] %>%
              group_by(id) %>% summarise(count = n())

number_replies <- data[data$type == "Reply", ] %>%
              group_by(id) %>% summarise(count = n())

print(number_posts)
print(number_tweets)
print(number_replies)

Calculating the percentage of tweets and replies based on all posts

# Merging the counts of tweets (count.y) and replies (count) with the count of posts (count.x)
data_ratio <- merge(number_posts, number_tweets, by = "id", all = TRUE)
data_ratio <- merge(data_ratio, number_replies, by = "id", all = TRUE)


data_ratio$percentage_tweets <- (data_ratio$count.y / data_ratio$count.x) * 100
data_ratio$percentage_replies <- (data_ratio$count / data_ratio$count.x) * 100

data_ratio <- data_ratio[, c("id", "percentage_tweets", "percentage_replies")]

print(data_ratio)

NA removal

Function to visualize the number of NAs in all columns

na_count <- function(){
  # Counting the number of NA values for each column
  na_count <- colSums(is.na(data))
  
  # Creating a new data frame with the NA counts
  na_counts_table <- data.frame(Column = names(na_count), NA_Count = na_count)
  
  print(na_counts_table)
}

Calculations of view, favourite, retweet and reply percentiles and visualization of NAs in all columns

data <- data %>%
  group_by(id) %>%
  mutate(view_percentile = ntile(view_count, 100),
         favorite_percentile = ntile(favorite_count, 100),
         retweet_percentile = ntile(retweet_count, 100),
         reply_percentile = ntile(reply_count, 100)) %>%
  rowwise() %>%
  mutate(avg_percentile = mean(c(view_percentile, favorite_percentile, retweet_percentile, reply_percentile), na.rm = TRUE))

na_count()

data_percentile <- data[, c("id", "view_percentile", "favorite_percentile", "retweet_percentile", "reply_percentile", "avg_percentile")]

print(data_percentile)

Calculation of the maximum number of views for each HEI

max_view_counts <- tapply(data$view_count, data$id, max, na.rm = TRUE)

print(max_view_counts)
      duke.csv       epfl.csv        goe.csv    harvard.csv  leicester.csv manchester.csv        mit.csv         sb.csv   stanford.csv    trinity.csv 
        307969         105095          15455        2982704          47838         317086        7604544         607498         222593         205333 
        wv.csv       yale.csv 
        109265         143108 

Removal of NAs

# From view count
data$view_count <- ifelse(
  is.na(data$view_count),
  round(max_view_counts[data$id] * (data$avg_percentile / 100)),
  data$view_count)

# From view percentile
data$view_percentile <- ifelse(
  is.na(data$view_percentile),
  data$avg_percentile,
  data$view_percentile)

Visualization of NAs in all columns

na_count()

For now we’ll be only looking at tweets

data_tweets <- data[data$type == "Tweet", ]

data_tweets

Function to calculate average posts

average_tweets <- function(timeframe = "days"){
  # Calculation of the timeframe between earliest and latest post for each HEI
  date_range <- data_tweets %>%
    group_by(id) %>%
    summarise(min_date = min(created_at),
              max_date = max(created_at)) %>%
    mutate(num_days = as.numeric(difftime(max_date, min_date, units = timeframe)))
  
  # Naming the column respecting the timeframe
  column_name <- paste0("avg_tweets_per_", timeframe)
  
  # Calculation of the number of tweets per day for each HEI
  tweets_per_timeframe <- number_tweets %>%
    left_join(date_range, by = "id") %>%
    mutate(!!column_name := count / num_days)
  
  print(tweets_per_timeframe)
  return(tweets_per_timeframe)
}
tweets_per_day <- average_tweets()
tweets_per_week <- average_tweets(timeframe = "weeks")

Plot for the average number of tweets per day for each HEI

barplot(tweets_per_day$avg_tweets_per_days,
        names.arg = tweets_per_day$id,
        main = "Average Tweets per Day",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_day$avg_tweets_per_days) + 1),
        las = 2,
        col = "#3498DB")

# Adding text labels over each bar and aligning it with the center of each bar 
text(x = barplot(tweets_per_day$avg_tweets_per_days, plot = FALSE),
     y = tweets_per_day$avg_tweets_per_days,
     labels = round(tweets_per_day$avg_tweets_per_days, 2),
     pos = 3)

Plot for the average number of tweets per week for each HEI

barplot(tweets_per_week$avg_tweets_per_weeks,
        names.arg = tweets_per_week$id,
        main = "Average Tweets per Week",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_week$avg_tweets_per_weeks) + 5),
        las = 2,
        col = "#E74C3C")

text(x = barplot(tweets_per_week$avg_tweets_per_weeks, plot = FALSE),
     y = tweets_per_week$avg_tweets_per_weeks,
     labels = round(tweets_per_week$avg_tweets_per_weeks, 2),
     pos = 3)

Defining the intervals of time for the academic year

intervals <- list(
  interval1 = as.POSIXct(c("2022-08-31", "2022-12-15")),
  interval2 = as.POSIXct(c("2023-01-04", "2023-04-01")),
  interval3 = as.POSIXct(c("2023-04-14", "2023-06-15"))
)

Function to check if a date falls within a given interval of time and apply appropriate Boolean

check_interval <- function(date) {
  for (i in 1:length(intervals)) {
    interval_start <- intervals[[i]][1]
    interval_end <- intervals[[i]][2]
    if (date >= interval_start & date <= interval_end) {
      return(TRUE)
    }
  }
  return(FALSE)
}
data_tweets$academic_year <- sapply(data_tweets$created_at, check_interval)
print(data.frame(id = data_tweets$id, academic_year = data_tweets$academic_year))

Plot for the number of tweets per timeframe of either vacation or academic time

barplot(table(data_tweets$academic_year),
        main = "Number of Tweets per Timeframe",
        xlab = "Time",
        ylab = "Count",
        ylim = c(0, max(table(data_tweets$academic_year)) + 1000),
        names.arg = c("Vacation", "Academic"),
        col = c("#8E44AD", "#F1C40F"))

text(x = barplot(data_tweets$academic_year, plot = FALSE), 
     y = table(data_tweets$academic_year) + 0.5, 
     labels = table(data_tweets$academic_year), 
     pos = 3)

Function to count number of tweets and average per day

analyze_tweets <- function(academic_year_filter = TRUE) {
  # Filtering the data based on the academic_year_filter
  filtered_data <- data_tweets %>%
    filter(academic_year == academic_year_filter)
  
  # Count of days for each HEI
  unique_days <- filtered_data %>%
    group_by(id) %>%
    summarise(unique_days = n_distinct(as.Date(created_at)))
  
  # Count of tweets for each HEI
  number_tweets_boolean <- filtered_data %>%
    group_by(id) %>%
    summarise(count = n())
  
  # Naming the column respecting the time period
  year <- ifelse(academic_year_filter, "academic_time", "vacation_time")
  column_name <- paste0("avg_tweets_in_", year)
  
  # Combination of data and calculation of average posts per day
  combined_data <- left_join(unique_days, number_tweets_boolean, by = "id")
  combined_data <- combined_data %>%
    mutate(!!column_name := count / unique_days)
  
  print(combined_data)
  return(combined_data)
}
data_tweets_academic <- analyze_tweets()
data_tweets_vacations <- analyze_tweets(academic_year_filter = FALSE)

Plot for the average number of tweets during academic time for each HEI

barplot(data_tweets_academic$avg_tweets_in_academic_time,
        names.arg = data_tweets_academic$id,
        main = "Average Tweets during Academic Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_academic$avg_tweets_in_academic_time) + 5),
        las = 2,
        col = "#34495E")

text(x = barplot(data_tweets_academic$avg_tweets_in_academic_time, plot = FALSE),
     y = data_tweets_academic$avg_tweets_in_academic_time,
     labels = round(data_tweets_academic$avg_tweets_in_academic_time, 2),
     pos = 3)

Plot for the average number of tweets during vacation time for each HEI

barplot(data_tweets_vacations$avg_tweets_in_vacation_time,
        names.arg = data_tweets_vacations$id,
        main = "Average Tweets during Vacation Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_vacations$avg_tweets_in_vacation_time) + 5),
        las = 2,
        col = "#D35400")

text(x = barplot(data_tweets_vacations$avg_tweets_in_vacation_time, plot = FALSE),
     y = data_tweets_vacations$avg_tweets_in_vacation_time,
     labels = round(data_tweets_vacations$avg_tweets_in_vacation_time, 2),
     pos = 3)

Data preparation for day of the week

# Creating new table that contains a new column for the day of the week
data_tweets_days <- data_tweets %>%
  mutate(day_of_week = weekdays(created_at))

# Selecting only the id, created_at, and day_of_week columns for the new table
data_tweets_days <- data_tweets_days %>%
  select(id, created_at, day_of_week)

print(data_tweets_days)
# Grouping by id and day_of_week, then counting the number of tweets
number_tweets_days <- data_tweets_days %>%
  group_by(id, day_of_week) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# Grouping by id, day_of_week and day created at, then counting the number of tweets
number_tweets_per_day <- data_tweets_days %>%
  mutate(created_date = as.Date(created_at)) %>%
  group_by(id, day_of_week, created_date) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id', 'day_of_week'. You can override using the `.groups` argument.
# Finding for each HEI the average count of tweets per day
average_number_tweets_per_day <- number_tweets_per_day %>%
  group_by(id, day_of_week) %>%
  summarise(average_count = mean(count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(number_tweets_days)

Highest and lowest tweets

# Finding the HEI with the lowest count of tweets per day
lowest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_min(order_by = count) %>%
  select(day_of_week, id, count)

# Finding the HEI with the highest count of tweets per day
highest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_max(order_by = count) %>%
  select(day_of_week, id, count)

# Combine the results
high_low_HEI <- bind_rows(lowest_count, highest_count) %>%
  arrange(day_of_week)

print(high_low_HEI)

Plot for the lowest and highest count of tweets per day for each day of the week

ggplot(high_low_HEI, aes(x = day_of_week, y = count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = count),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3) +
  labs(title = "Lowest and Highest Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Average of tweets

# Finding the HEI with lowest and highest averaged count of tweets per day
high_low_average_HEIs <- average_number_tweets_per_day %>%
  group_by(day_of_week) %>%
  filter(average_count == max(average_count) | average_count == min(average_count)) %>%
  arrange(day_of_week, ifelse(average_count == min(average_count), average_count, -average_count))

print(high_low_average_HEIs)

Plot for the highest and lowest average count of tweets per day for each day of the week

ggplot(high_low_average_HEIs, aes(x = day_of_week, y = average_count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = round(average_count, 2)),
            position = position_dodge(width = 0.7),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Average Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Average Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Views Likes Retweets and Replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_tweets <- data_tweets %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_tweets)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_tweets_stats <- types_of_tweets %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_tweets_stats)

Function for piechart creation for views, likes and replies

pie_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  hei_data <- types_of_tweets %>%
    filter(id == target_id)
  
  # Calculating total views for each media type for the specific HEI
  hei_media <- hei_data %>%
    group_by(media_type) %>%
    summarise(total_views = sum(views),
              total_likes = sum(likes),
              total_replies = sum(replies))
  
  # Calculating the percentage of views for each media type for the specific HEI
  hei_media$percentage_view <- hei_media$total_views / sum(hei_media$total_views) * 100
  hei_media$percentage_like <- hei_media$total_likes / sum(hei_media$total_likes) * 100
  hei_media$percentage_reply <- hei_media$total_replies / sum(hei_media$total_replies) * 100
  
  # Creating the pie chart for views
  hei_pie_chart_views <- ggplot(hei_media, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#2196F3", "animated_gif" = "#E67E22", "photo" = "#8E44AD", "video" = "#138D75")) +
    labs(title = paste("Views for each media type -", target_id))
  
  # Creating the pie chart for likes
  hei_pie_chart_likes <- ggplot(hei_media, aes(x = "", y = percentage_like, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_likes, "(", round(percentage_like, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#E91E63", "animated_gif" = "#4A148C", "photo" = "#90CAF9", "video" = "#00BFA5")) +
    labs(title = paste("Likes for each media type -", target_id))
  
  # Creating the pie chart for replies
  hei_pie_chart_replies <- ggplot(hei_media, aes(x = "", y = percentage_reply, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_replies, "(", round(percentage_reply, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#666600", "animated_gif" = "#99CCCC", "photo" = "#9966CC", "video" = "#330000")) +
    labs(title = paste("Replies for each media type -", target_id))
  
  # Print the pie charts
  print(hei_pie_chart_views)
  print(hei_pie_chart_likes)
  print(hei_pie_chart_replies)
}

Plot of piecharts for each HEI

pie_maker()

pie_maker("epfl.csv")

pie_maker("goe.csv")

pie_maker("harvard.csv")

pie_maker("leicester.csv")

pie_maker("manchester.csv")

pie_maker("mit.csv")

pie_maker("sb.csv")

pie_maker("stanford.csv")

pie_maker("trinity.csv")

pie_maker("wv.csv")

pie_maker("yale.csv")

# Calculation of like_ratio and replies_ratio percentages
ratios_tweets_table <- total_tweets_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_tweets_ratios <- ratios_tweets_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_tweets_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_tweets_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replys Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replys Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#2196F3", "Replys Ratio" = "#F44336")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

# Table with averages of views, likes, retweets and replies
types_of_tweets_per_tweet <- types_of_tweets %>%
                        group_by(id, media_type) %>%
                        summarise(avg_views = mean(views / count),
                                  avg_likes = mean(likes / count),
                                  avg_retweets = mean(retweets / count),
                                  avg_replies = mean(replies / count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(types_of_tweets_per_tweet)
# Grouping by HEI and calculating the average values of views, likes and replies across all media types
total_average_stats <- types_of_tweets_per_tweet %>%
  group_by(id) %>%
  summarise(avg_views = sum(avg_views),
            avg_likes = sum(avg_likes),
            avg_replies = sum(avg_replies))

print(total_average_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_average_table <- total_average_stats %>%
  mutate(like_ratio = avg_likes / avg_views * 100,
         replies_ratio = avg_replies / avg_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_average_ratios <- ratios_average_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_average_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_average_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replies Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replies Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#330066", "Replies Ratio" = "#FF6666")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Hashtags

# Table with number of unique hashtags
unique_hashtags <- data_tweets %>%
                group_by(id) %>%
                summarise(count = n(),
                          unique_hashtags = length(unique(hashtags)))

print(unique_hashtags)

Plot for the count of unique hashtags for each HEI

barplot(unique_hashtags$unique_hashtags,
        names.arg = unique_hashtags$id,
        main = "Unique Hashtags for Each HEI",
        xlab = "HEI",
        ylab = "Count of Unique Hashtags",
        ylim = c(0, max(unique_hashtags$unique_hashtags) + 50),
        las = 2,
        col= "#16A085")

text(x = barplot(unique_hashtags$unique_hashtags, plot = FALSE),
     y = unique_hashtags$unique_hashtags,
     labels = round(unique_hashtags$unique_hashtags, 2),
     pos = 3)

Heatmaps

# Create column hour from created_at
data_tweets_days$created_hour <- as.numeric(format(data_tweets_days$created_at, "%H"))

Function to plot heatmap for various HEIs

heatmap_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  target_data <- data_tweets_days %>%
    filter(id == target_id)
  
  # Grouping by day of the week and hour, and counting the number of tweets
  tweet_counts <- target_data %>%
    group_by(day_of_week, created_hour) %>%
    summarise(num_tweets = n())
  
  # Plotting heatmap
  ggplot(tweet_counts, aes(x = day_of_week, y = created_hour, fill = num_tweets)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "blue") +
    labs(title = paste("Tweet Heatmap for", target_id),
         x = "Day of the week",
         y = "Hour of the day")
}

heatmap_maker()
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("epfl.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("goe.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("harvard.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("leicester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("manchester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("mit.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("sb.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("stanford.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("trinity.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("wv.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("yale.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

Text

data_tweets_content <- data_tweets %>%
            select(id, text)

# Counting number of words
data_tweets_content <- data_tweets_content %>%
  mutate(num_words = lengths(strsplit(text, "\\s+")))

print(data_tweets_content)

# Grouping by HEI and calculate average, minimum, and maximum values of number of words
data_tweets_content_metrics <- data_tweets_content %>%
  group_by(id) %>%
  summarise(average_num_words = mean(num_words),
            min_num_words = min(num_words),
            max_num_words = max(num_words))
print(data_tweets_content_metrics)

Plot for the average, maximum and minimum values of words for each HEI

ggplot(data_tweets_content_metrics, aes(x = id, y = average_num_words)) +
  geom_point(aes(color = "Average")) +
  geom_errorbar(aes(ymin = min_num_words, ymax = max_num_words, color = "Range"), width = 0.2) +
  scale_color_manual(values = c("Average" = "#1976D2", "Range" = "#EF5350")) +
  labs(title = "Word Count Summary by HEI",
       x = "HEI",
       y = "Number of Words",
       color = "Metric") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Now replies

data_replies <- data[data$type == "Reply", ]

data_replies

Interactions to replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_replies <- data_replies %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_replies)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_replies_stats <- types_of_replies %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_replies_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_replies_table <- total_replies_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_replies_ratios <- ratios_replies_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_replies_ratios)

Clusters

# Creating table for cluster algorithms

# Joining attribute count (number of tweets) and unique_hashtags (number of unique hashtags) per HEI
cluster_table <- merge(select(unique_hashtags, id, unique_hashtags), select(number_tweets, id, count), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_days (average of tweets per day) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_day, id, avg_tweets_per_days), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_weeks (average of tweets per week) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_week, id, avg_tweets_per_weeks), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_academic_time (average of tweets during academic time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_academic, id, avg_tweets_in_academic_time), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_vacation_time (average of tweets during vacation time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_vacations, id, avg_tweets_in_vacation_time), by = "id", all=TRUE)

# Joining attribute total_views (total number of views), total_likes (total number of likes) and total_replies (total number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_tweets_stats, id, total_views, total_likes, total_replies), by = "id", all=TRUE)

# Renaming attribute like_ratio to total_like_ratio and replies_ratio to total_replies_ratio
cluster_table <- merge(cluster_table, select(hei_tweets_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(total_like_ratio = like_ratio, 
         total_replies_ratio = replies_ratio)

# Joining attribute avg_views (average number of views), avg_likes (average number of likes) and avg_replies (average number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_average_stats, id, avg_views, avg_likes, avg_replies), by = "id", all=TRUE)

# Renaming attribute like_ratio to avg_like_ratio and replies_ratio to avg_replies_ratio
cluster_table <- merge(cluster_table, select(hei_average_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(avg_like_ratio = like_ratio, 
         avg_replies_ratio = replies_ratio)

print(cluster_table)

Function for cluster method

cluster_maker <- function(seed = 123, num_clusters = 3, table){
  set.seed(123)
  
  # Excluding id column for clustering
  cluster_data <- select(table, -id)
  
  # Scaling the data for kmeans method
  scaled_data <- scale(cluster_data)
  
  kmeans_result <- kmeans(scaled_data, centers = num_clusters)
  
  print(kmeans_result$centers)
  print(kmeans_result$cluster)
  
  return(kmeans_result)
}

Function to add ids to better visualize results

cluster_id_maker <- function(kmeans_result, table){
  # Merging the cluster assignments with the original data
  cluster_assignments <- data.frame(id = table$id, cluster = kmeans_result$cluster)

  print(cluster_assignments)
  plot(kmeans_result$cluster)
}

Three clusters with seed 123

cluster_123_3 <- cluster_maker(table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   2.0971401  1.93751241
3       0.1740030 -0.1713844          -0.1731538           -0.1731538                  -0.1731809                  -0.1913303  -0.4153661 -0.43904812
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067        2.0996788           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     1.6810073       -0.7177677          -0.4777518  1.7460208  1.4388566   1.1599670     -0.6396304        -0.4169707
3    -0.4350468       -0.0737937          -0.2347759 -0.3494817 -0.5342368  -0.5149084     -0.1470868        -0.2520126
 [1] 3 3 3 2 3 3 2 3 1 3 3 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Seven clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 7, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1     -0.54099099 -0.7980288          -0.7998764           -0.7998764                  -0.8566746                  -0.7686479  -0.4659280 -0.34870651
2     -0.33665384  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3      2.06849496  0.3163906           0.3164848            0.3164848                   0.3487604                   0.3235393  -0.4385686 -0.45795609
4     -0.36345215 -0.5258824          -0.5295450           -0.5295450                  -0.5074127                  -0.5879623  -0.3946334 -0.54854585
5     -0.59793741  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
6     -0.63143530 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
7     -0.03517279  1.5243464           1.5265794            1.5265794                   1.4868514                   1.5200937  -0.3507682 -0.14392452
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.44336928        1.2911568          0.07105937 -0.3861273 -0.3579270  -0.4728730      0.8212504       -0.10327394
2    2.91587745       -0.6417247         -0.40143781  0.3936984  1.4654190   1.5919118     -0.5339425       -0.30190583
3   -0.46641612       -0.2251792         -0.13401717 -0.3929316 -0.6804893  -0.5991306     -0.2493630       -0.12958214
4   -0.51767840       -0.6173697         -0.47022920 -0.3036546 -0.5504837  -0.5144803     -0.5587944       -0.43018681
5    0.44613716       -0.7938108         -0.55406573  3.0983433  1.4122942   0.7280222     -0.7453183       -0.53203567
6    0.55340665        2.0996788          3.06848680 -0.3467066  1.9304181   2.3142417      2.6030420        3.10205460
7   -0.02513687       -0.3266195         -0.10615086 -0.3725990 -0.5293640  -0.4322473     -0.2323782       -0.08165369
 [1] 4 4 1 2 3 4 5 4 6 3 1 7
cluster_id_maker(cluster_123_6, table = cluster_table)

Five clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 5, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.4226318 -0.6165979          -0.6196555           -0.6196555                  -0.6238333                  -0.6481908  -0.4183983 -0.48193274
5      -0.5979374  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067       2.09967875           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     2.9158774      -0.64172468          -0.4014378  0.3936984  1.4654190   1.5919118     -0.5339425        -0.3019058
3    -0.3193230      -0.25899262          -0.1247284 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.4929087       0.01880576          -0.2897997 -0.3311455 -0.4862981  -0.5006112     -0.0987795        -0.3212159
5     0.4461372      -0.79381078          -0.5540657  3.0983433  1.4122942   0.7280222     -0.7453183        -0.5320357
 [1] 4 4 4 2 3 4 5 4 1 3 4 3
cluster_id_maker(cluster_123_6, table = cluster_table)

Four clusters with seed 4855

cluster_123_3 <- cluster_maker(seed = 4855, num_clusters = 4, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   2.0971401  1.93751241
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.4226318 -0.6165979          -0.6196555           -0.6196555                  -0.6238333                  -0.6481908  -0.4183983 -0.48193274
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1     0.5534067       2.09967875           3.0684868 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
2     1.6810073      -0.71776773          -0.4777518  1.7460208  1.4388566   1.1599670     -0.6396304        -0.4169707
3    -0.3193230      -0.25899262          -0.1247284 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.4929087       0.01880576          -0.2897997 -0.3311455 -0.4862981  -0.5006112     -0.0987795        -0.3212159
 [1] 4 4 4 2 3 4 2 4 1 3 4 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 4855

cluster_123_6 <- cluster_maker(seed = 4855, num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.5409910 -0.7980288          -0.7998764           -0.7998764                  -0.8566746                  -0.7686479  -0.4659280 -0.34870651
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   1.6390693  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.4093018 -0.35327890
4      -0.3634522 -0.5258824          -0.5295450           -0.5295450                  -0.5074127                  -0.5879623  -0.3946334 -0.54854585
5      -0.5979374  0.3734033           0.3706973            0.3706973                   0.1671229                   0.3341520   2.5552108  1.10975687
6      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.4559850  0.07640829
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1    -0.4433693        1.2911568          0.07105937 -0.3861273 -0.3579270  -0.4728730      0.8212504        -0.1032739
2     2.9158774       -0.6417247         -0.40143781  0.3936984  1.4654190   1.5919118     -0.5339425        -0.3019058
3    -0.3193230       -0.2589926         -0.12472840 -0.3861541 -0.6301142  -0.5435029     -0.2437014        -0.1136060
4    -0.5176784       -0.6173697         -0.47022920 -0.3036546 -0.5504837  -0.5144803     -0.5587944        -0.4301868
5     0.4461372       -0.7938108         -0.55406573  3.0983433  1.4122942   0.7280222     -0.7453183        -0.5320357
6     0.5534067        2.0996788          3.06848680 -0.3467066  1.9304181   2.3142417      2.6030420         3.1020546
 [1] 4 4 1 2 3 4 5 4 6 3 1 3
cluster_id_maker(cluster_123_6, table = cluster_table)

LS0tCnRpdGxlOiAiUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIFRPRE8gIAojIHBhc3NhciB0YWJlbGFzIHByYSBjc3YodG9kYXM/KSwgcmVuYW1lIGJsYW5rIGhhc2h0YWcvbWVkaWEsIGp1bnRhciB2YWxvcmVzIGRlIHJlcGx5IHByYSBjbHVzdGVyPyBwYXIobWZyb3c9YygxLDIpKQoKIyMjIEZvciBSIGJlZ2lubmVycwpOZXcgY2h1bmsgKkN0cmwrQWx0K0kqCgpFeGVjdXRlIGNodW5rICpDdHJsK1NoaWZ0K0VudGVyKgoKRXhlY3V0ZSBhbGwgY2h1bmtzICpDdHJsK0FsdCtSKgoKSFRNTCBwcmV2aWV3ICpDdHJsK1NoaWZ0K0sqCgojIExpYnJhcnkgcHJlcGFyYXRpb25zCgpgYGB7cn0KbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShzdGF0cykKYGBgCgojIERhdGEgSW1wb3J0CgpgYGB7cn0KZGF0YSA8LSByZWFkLmNzdigifi80eWVhci8yc2VtZXN0ZXIvZHRJSS9DU1ZzL0hFSXMuY3N2IiwKICAgICAgICAgICAgICAgICBjb2xDbGFzc2VzID0gYyh0d2VldF9pZCA9ICJjaGFyYWN0ZXIiKSkKCiMgTW9kaWZ5aW5nIGNyZWF0ZWRfYXQgdHlwZSBzbyB0aGF0IGF0dHJpYnV0ZSBjYW4gYmUgdXNlZCBtb3JlIGVhc2lseSAKZGF0YSRjcmVhdGVkX2F0IDwtIGFzLlBPU0lYY3QoZGF0YSRjcmVhdGVkX2F0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQ9ICIlWS0lbS0lZFQlSDolTTolUyIsIHR6PSJVVEMiKQoKI1ZpZXcoZGF0YSkKc3VtbWFyeShkYXRhKQpgYGAKCiMgSW5pdGlhbCBEYXRhIFByZXBhcmF0aW9uCgpgYGB7cn0KIyBDb3VudCBvZiBob3cgbWFueSBlbnRyaWVzIGVhY2ggSEVJIGhhcwpudW1iZXJfaW50ZXJhY3Rpb25zIDwtIGRhdGEgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfaW50ZXJhY3Rpb25zCmBgYAoKIyBTaW5jZSBjb21wbHV0ZW5zZSBvbmx5IGhhcyAxIGVudHJ5IHdlIGNhbid0IGxlYXJuIGFueXRoaW5nIGZyb20gaXQsIHNvIHdlIHJlbW92ZWQgaXQKCmBgYHtyfQpkYXRhIDwtIGRhdGFbZGF0YSRpZCAhPSAiY29tcGx1dGVuc2UuY3N2IiwgXQpgYGAKCiMgVmlzdWFsaXphdGlvbiBvZiBudW1iZXIgYWxsIHBvc3RzLCBqdXN0IHR3ZWV0cyBhbmQganVzdCByZXBsaWVzCgpgYGB7cn0KbnVtYmVyX3Bvc3RzIDwtIGRhdGEgJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfdHdlZXRzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJUd2VldCIsIF0gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpudW1iZXJfcmVwbGllcyA8LSBkYXRhW2RhdGEkdHlwZSA9PSAiUmVwbHkiLCBdICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKcHJpbnQobnVtYmVyX3Bvc3RzKQpwcmludChudW1iZXJfdHdlZXRzKQpwcmludChudW1iZXJfcmVwbGllcykKYGBgCgojIENhbGN1bGF0aW5nIHRoZSBwZXJjZW50YWdlIG9mIHR3ZWV0cyBhbmQgcmVwbGllcyBiYXNlZCBvbiBhbGwgcG9zdHMKCmBgYHtyfQojIE1lcmdpbmcgdGhlIGNvdW50cyBvZiB0d2VldHMgKGNvdW50LnkpIGFuZCByZXBsaWVzIChjb3VudCkgd2l0aCB0aGUgY291bnQgb2YgcG9zdHMgKGNvdW50LngpCmRhdGFfcmF0aW8gPC0gbWVyZ2UobnVtYmVyX3Bvc3RzLCBudW1iZXJfdHdlZXRzLCBieSA9ICJpZCIsIGFsbCA9IFRSVUUpCmRhdGFfcmF0aW8gPC0gbWVyZ2UoZGF0YV9yYXRpbywgbnVtYmVyX3JlcGxpZXMsIGJ5ID0gImlkIiwgYWxsID0gVFJVRSkKCgpkYXRhX3JhdGlvJHBlcmNlbnRhZ2VfdHdlZXRzIDwtIChkYXRhX3JhdGlvJGNvdW50LnkgLyBkYXRhX3JhdGlvJGNvdW50LngpICogMTAwCmRhdGFfcmF0aW8kcGVyY2VudGFnZV9yZXBsaWVzIDwtIChkYXRhX3JhdGlvJGNvdW50IC8gZGF0YV9yYXRpbyRjb3VudC54KSAqIDEwMAoKZGF0YV9yYXRpbyA8LSBkYXRhX3JhdGlvWywgYygiaWQiLCAicGVyY2VudGFnZV90d2VldHMiLCAicGVyY2VudGFnZV9yZXBsaWVzIildCgpwcmludChkYXRhX3JhdGlvKQpgYGAKCiMgTkEgcmVtb3ZhbAoKIyBGdW5jdGlvbiB0byB2aXN1YWxpemUgdGhlIG51bWJlciBvZiBOQXMgaW4gYWxsIGNvbHVtbnMKCmBgYHtyfQpuYV9jb3VudCA8LSBmdW5jdGlvbigpewogICMgQ291bnRpbmcgdGhlIG51bWJlciBvZiBOQSB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uCiAgbmFfY291bnQgPC0gY29sU3Vtcyhpcy5uYShkYXRhKSkKICAKICAjIENyZWF0aW5nIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCB0aGUgTkEgY291bnRzCiAgbmFfY291bnRzX3RhYmxlIDwtIGRhdGEuZnJhbWUoQ29sdW1uID0gbmFtZXMobmFfY291bnQpLCBOQV9Db3VudCA9IG5hX2NvdW50KQogIAogIHByaW50KG5hX2NvdW50c190YWJsZSkKfQpgYGAKCiMgQ2FsY3VsYXRpb25zIG9mIHZpZXcsIGZhdm91cml0ZSwgcmV0d2VldCBhbmQgcmVwbHkgcGVyY2VudGlsZXMgYW5kIHZpc3VhbGl6YXRpb24gb2YgTkFzIGluIGFsbCBjb2x1bW5zCgpgYGB7cn0KZGF0YSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBtdXRhdGUodmlld19wZXJjZW50aWxlID0gbnRpbGUodmlld19jb3VudCwgMTAwKSwKICAgICAgICAgZmF2b3JpdGVfcGVyY2VudGlsZSA9IG50aWxlKGZhdm9yaXRlX2NvdW50LCAxMDApLAogICAgICAgICByZXR3ZWV0X3BlcmNlbnRpbGUgPSBudGlsZShyZXR3ZWV0X2NvdW50LCAxMDApLAogICAgICAgICByZXBseV9wZXJjZW50aWxlID0gbnRpbGUocmVwbHlfY291bnQsIDEwMCkpICU+JQogIHJvd3dpc2UoKSAlPiUKICBtdXRhdGUoYXZnX3BlcmNlbnRpbGUgPSBtZWFuKGModmlld19wZXJjZW50aWxlLCBmYXZvcml0ZV9wZXJjZW50aWxlLCByZXR3ZWV0X3BlcmNlbnRpbGUsIHJlcGx5X3BlcmNlbnRpbGUpLCBuYS5ybSA9IFRSVUUpKQoKbmFfY291bnQoKQoKZGF0YV9wZXJjZW50aWxlIDwtIGRhdGFbLCBjKCJpZCIsICJ2aWV3X3BlcmNlbnRpbGUiLCAiZmF2b3JpdGVfcGVyY2VudGlsZSIsICJyZXR3ZWV0X3BlcmNlbnRpbGUiLCAicmVwbHlfcGVyY2VudGlsZSIsICJhdmdfcGVyY2VudGlsZSIpXQoKcHJpbnQoZGF0YV9wZXJjZW50aWxlKQpgYGAKCiMgQ2FsY3VsYXRpb24gb2YgdGhlIG1heGltdW0gbnVtYmVyIG9mIHZpZXdzIGZvciBlYWNoIEhFSQoKYGBge3J9Cm1heF92aWV3X2NvdW50cyA8LSB0YXBwbHkoZGF0YSR2aWV3X2NvdW50LCBkYXRhJGlkLCBtYXgsIG5hLnJtID0gVFJVRSkKCnByaW50KG1heF92aWV3X2NvdW50cykKYGBgCgojIFJlbW92YWwgb2YgTkFzCgpgYGB7cn0KIyBGcm9tIHZpZXcgY291bnQKZGF0YSR2aWV3X2NvdW50IDwtIGlmZWxzZSgKICBpcy5uYShkYXRhJHZpZXdfY291bnQpLAogIHJvdW5kKG1heF92aWV3X2NvdW50c1tkYXRhJGlkXSAqIChkYXRhJGF2Z19wZXJjZW50aWxlIC8gMTAwKSksCiAgZGF0YSR2aWV3X2NvdW50KQoKIyBGcm9tIHZpZXcgcGVyY2VudGlsZQpkYXRhJHZpZXdfcGVyY2VudGlsZSA8LSBpZmVsc2UoCiAgaXMubmEoZGF0YSR2aWV3X3BlcmNlbnRpbGUpLAogIGRhdGEkYXZnX3BlcmNlbnRpbGUsCiAgZGF0YSR2aWV3X3BlcmNlbnRpbGUpCmBgYAoKIyBWaXN1YWxpemF0aW9uIG9mIE5BcyBpbiBhbGwgY29sdW1ucwoKYGBge3J9Cm5hX2NvdW50KCkKYGBgCgojIEZvciBub3cgd2UnbGwgYmUgb25seSBsb29raW5nIGF0IHR3ZWV0cwoKYGBge3J9CmRhdGFfdHdlZXRzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJUd2VldCIsIF0KCmRhdGFfdHdlZXRzCmBgYAoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgYXZlcmFnZSBwb3N0cwoKYGBge3J9CmF2ZXJhZ2VfdHdlZXRzIDwtIGZ1bmN0aW9uKHRpbWVmcmFtZSA9ICJkYXlzIil7CiAgIyBDYWxjdWxhdGlvbiBvZiB0aGUgdGltZWZyYW1lIGJldHdlZW4gZWFybGllc3QgYW5kIGxhdGVzdCBwb3N0IGZvciBlYWNoIEhFSQogIGRhdGVfcmFuZ2UgPC0gZGF0YV90d2VldHMgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UobWluX2RhdGUgPSBtaW4oY3JlYXRlZF9hdCksCiAgICAgICAgICAgICAgbWF4X2RhdGUgPSBtYXgoY3JlYXRlZF9hdCkpICU+JQogICAgbXV0YXRlKG51bV9kYXlzID0gYXMubnVtZXJpYyhkaWZmdGltZShtYXhfZGF0ZSwgbWluX2RhdGUsIHVuaXRzID0gdGltZWZyYW1lKSkpCiAgCiAgIyBOYW1pbmcgdGhlIGNvbHVtbiByZXNwZWN0aW5nIHRoZSB0aW1lZnJhbWUKICBjb2x1bW5fbmFtZSA8LSBwYXN0ZTAoImF2Z190d2VldHNfcGVyXyIsIHRpbWVmcmFtZSkKICAKICAjIENhbGN1bGF0aW9uIG9mIHRoZSBudW1iZXIgb2YgdHdlZXRzIHBlciBkYXkgZm9yIGVhY2ggSEVJCiAgdHdlZXRzX3Blcl90aW1lZnJhbWUgPC0gbnVtYmVyX3R3ZWV0cyAlPiUKICAgIGxlZnRfam9pbihkYXRlX3JhbmdlLCBieSA9ICJpZCIpICU+JQogICAgbXV0YXRlKCEhY29sdW1uX25hbWUgOj0gY291bnQgLyBudW1fZGF5cykKICAKICBwcmludCh0d2VldHNfcGVyX3RpbWVmcmFtZSkKICByZXR1cm4odHdlZXRzX3Blcl90aW1lZnJhbWUpCn0KYGBgCgpgYGB7cn0KdHdlZXRzX3Blcl9kYXkgPC0gYXZlcmFnZV90d2VldHMoKQp0d2VldHNfcGVyX3dlZWsgPC0gYXZlcmFnZV90d2VldHModGltZWZyYW1lID0gIndlZWtzIikKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgcGVyIGRheSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsCiAgICAgICAgbmFtZXMuYXJnID0gdHdlZXRzX3Blcl9kYXkkaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFR3ZWV0cyBwZXIgRGF5IiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heCh0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzKSArIDEpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiMzNDk4REIiKQoKIyBBZGRpbmcgdGV4dCBsYWJlbHMgb3ZlciBlYWNoIGJhciBhbmQgYWxpZ25pbmcgaXQgd2l0aCB0aGUgY2VudGVyIG9mIGVhY2ggYmFyIAp0ZXh0KHggPSBiYXJwbG90KHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMsCiAgICAgbGFiZWxzID0gcm91bmQodHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cywgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgcGVyIHdlZWsgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdCh0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsCiAgICAgICAgbmFtZXMuYXJnID0gdHdlZXRzX3Blcl93ZWVrJGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBUd2VldHMgcGVyIFdlZWsiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFR3ZWV0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcykgKyA1KSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjRTc0QzNDIikKCnRleHQoeCA9IGJhcnBsb3QodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSB0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsCiAgICAgbGFiZWxzID0gcm91bmQodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgRGVmaW5pbmcgdGhlIGludGVydmFscyBvZiB0aW1lIGZvciB0aGUgYWNhZGVtaWMgeWVhcgoKYGBge3J9CmludGVydmFscyA8LSBsaXN0KAogIGludGVydmFsMSA9IGFzLlBPU0lYY3QoYygiMjAyMi0wOC0zMSIsICIyMDIyLTEyLTE1IikpLAogIGludGVydmFsMiA9IGFzLlBPU0lYY3QoYygiMjAyMy0wMS0wNCIsICIyMDIzLTA0LTAxIikpLAogIGludGVydmFsMyA9IGFzLlBPU0lYY3QoYygiMjAyMy0wNC0xNCIsICIyMDIzLTA2LTE1IikpCikKYGBgCgojIEZ1bmN0aW9uIHRvIGNoZWNrIGlmIGEgZGF0ZSBmYWxscyB3aXRoaW4gYSBnaXZlbiBpbnRlcnZhbCBvZiB0aW1lIGFuZCBhcHBseSBhcHByb3ByaWF0ZSBCb29sZWFuCgpgYGB7cn0KY2hlY2tfaW50ZXJ2YWwgPC0gZnVuY3Rpb24oZGF0ZSkgewogIGZvciAoaSBpbiAxOmxlbmd0aChpbnRlcnZhbHMpKSB7CiAgICBpbnRlcnZhbF9zdGFydCA8LSBpbnRlcnZhbHNbW2ldXVsxXQogICAgaW50ZXJ2YWxfZW5kIDwtIGludGVydmFsc1tbaV1dWzJdCiAgICBpZiAoZGF0ZSA+PSBpbnRlcnZhbF9zdGFydCAmIGRhdGUgPD0gaW50ZXJ2YWxfZW5kKSB7CiAgICAgIHJldHVybihUUlVFKQogICAgfQogIH0KICByZXR1cm4oRkFMU0UpCn0KYGBgCgpgYGB7cn0KZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciA8LSBzYXBwbHkoZGF0YV90d2VldHMkY3JlYXRlZF9hdCwgY2hlY2tfaW50ZXJ2YWwpCnByaW50KGRhdGEuZnJhbWUoaWQgPSBkYXRhX3R3ZWV0cyRpZCwgYWNhZGVtaWNfeWVhciA9IGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpKQpgYGAKCiMgUGxvdCBmb3IgdGhlIG51bWJlciBvZiB0d2VldHMgcGVyIHRpbWVmcmFtZSBvZiBlaXRoZXIgdmFjYXRpb24gb3IgYWNhZGVtaWMgdGltZQoKYGBge3J9CmJhcnBsb3QodGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciksCiAgICAgICAgbWFpbiA9ICJOdW1iZXIgb2YgVHdlZXRzIHBlciBUaW1lZnJhbWUiLAogICAgICAgIHhsYWIgPSAiVGltZSIsCiAgICAgICAgeWxhYiA9ICJDb3VudCIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHRhYmxlKGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpKSArIDEwMDApLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIlZhY2F0aW9uIiwgIkFjYWRlbWljIiksCiAgICAgICAgY29sID0gYygiIzhFNDRBRCIsICIjRjFDNDBGIikpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIsIHBsb3QgPSBGQUxTRSksIAogICAgIHkgPSB0YWJsZShkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSArIDAuNSwgCiAgICAgbGFiZWxzID0gdGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciksIAogICAgIHBvcyA9IDMpCmBgYAoKIyBGdW5jdGlvbiB0byBjb3VudCBudW1iZXIgb2YgdHdlZXRzIGFuZCBhdmVyYWdlIHBlciBkYXkKCmBgYHtyfQphbmFseXplX3R3ZWV0cyA8LSBmdW5jdGlvbihhY2FkZW1pY195ZWFyX2ZpbHRlciA9IFRSVUUpIHsKICAjIEZpbHRlcmluZyB0aGUgZGF0YSBiYXNlZCBvbiB0aGUgYWNhZGVtaWNfeWVhcl9maWx0ZXIKICBmaWx0ZXJlZF9kYXRhIDwtIGRhdGFfdHdlZXRzICU+JQogICAgZmlsdGVyKGFjYWRlbWljX3llYXIgPT0gYWNhZGVtaWNfeWVhcl9maWx0ZXIpCiAgCiAgIyBDb3VudCBvZiBkYXlzIGZvciBlYWNoIEhFSQogIHVuaXF1ZV9kYXlzIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgICBncm91cF9ieShpZCkgJT4lCiAgICBzdW1tYXJpc2UodW5pcXVlX2RheXMgPSBuX2Rpc3RpbmN0KGFzLkRhdGUoY3JlYXRlZF9hdCkpKQogIAogICMgQ291bnQgb2YgdHdlZXRzIGZvciBlYWNoIEhFSQogIG51bWJlcl90d2VldHNfYm9vbGVhbiA8LSBmaWx0ZXJlZF9kYXRhICU+JQogICAgZ3JvdXBfYnkoaWQpICU+JQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQogIAogICMgTmFtaW5nIHRoZSBjb2x1bW4gcmVzcGVjdGluZyB0aGUgdGltZSBwZXJpb2QKICB5ZWFyIDwtIGlmZWxzZShhY2FkZW1pY195ZWFyX2ZpbHRlciwgImFjYWRlbWljX3RpbWUiLCAidmFjYXRpb25fdGltZSIpCiAgY29sdW1uX25hbWUgPC0gcGFzdGUwKCJhdmdfdHdlZXRzX2luXyIsIHllYXIpCiAgCiAgIyBDb21iaW5hdGlvbiBvZiBkYXRhIGFuZCBjYWxjdWxhdGlvbiBvZiBhdmVyYWdlIHBvc3RzIHBlciBkYXkKICBjb21iaW5lZF9kYXRhIDwtIGxlZnRfam9pbih1bmlxdWVfZGF5cywgbnVtYmVyX3R3ZWV0c19ib29sZWFuLCBieSA9ICJpZCIpCiAgY29tYmluZWRfZGF0YSA8LSBjb21iaW5lZF9kYXRhICU+JQogICAgbXV0YXRlKCEhY29sdW1uX25hbWUgOj0gY291bnQgLyB1bmlxdWVfZGF5cykKICAKICBwcmludChjb21iaW5lZF9kYXRhKQogIHJldHVybihjb21iaW5lZF9kYXRhKQp9CmBgYAoKYGBge3J9CmRhdGFfdHdlZXRzX2FjYWRlbWljIDwtIGFuYWx5emVfdHdlZXRzKCkKZGF0YV90d2VldHNfdmFjYXRpb25zIDwtIGFuYWx5emVfdHdlZXRzKGFjYWRlbWljX3llYXJfZmlsdGVyID0gRkFMU0UpCmBgYAoKIyBQbG90IGZvciB0aGUgYXZlcmFnZSBudW1iZXIgb2YgdHdlZXRzIGR1cmluZyBhY2FkZW1pYyB0aW1lIGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QoZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lLAogICAgICAgIG5hbWVzLmFyZyA9IGRhdGFfdHdlZXRzX2FjYWRlbWljJGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBUd2VldHMgZHVyaW5nIEFjYWRlbWljIFRpbWUiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFR3ZWV0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSkgKyA1KSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbCA9ICIjMzQ0OTVFIikKCnRleHQoeCA9IGJhcnBsb3QoZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSBkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUsCiAgICAgbGFiZWxzID0gcm91bmQoZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHR3ZWV0cyBkdXJpbmcgdmFjYXRpb24gdGltZSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUsCiAgICAgICAgbmFtZXMuYXJnID0gZGF0YV90d2VldHNfdmFjYXRpb25zJGlkLAogICAgICAgIG1haW4gPSAiQXZlcmFnZSBUd2VldHMgZHVyaW5nIFZhY2F0aW9uIFRpbWUiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFR3ZWV0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiI0QzNTQwMCIpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUsCiAgICAgbGFiZWxzID0gcm91bmQoZGF0YV90d2VldHNfdmFjYXRpb25zJGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSwgMiksCiAgICAgcG9zID0gMykKYGBgCgojIERhdGEgcHJlcGFyYXRpb24gZm9yIGRheSBvZiB0aGUgd2VlayAKCmBgYHtyfQojIENyZWF0aW5nIG5ldyB0YWJsZSB0aGF0IGNvbnRhaW5zIGEgbmV3IGNvbHVtbiBmb3IgdGhlIGRheSBvZiB0aGUgd2VlawpkYXRhX3R3ZWV0c19kYXlzIDwtIGRhdGFfdHdlZXRzICU+JQogIG11dGF0ZShkYXlfb2Zfd2VlayA9IHdlZWtkYXlzKGNyZWF0ZWRfYXQpKQoKIyBTZWxlY3Rpbmcgb25seSB0aGUgaWQsIGNyZWF0ZWRfYXQsIGFuZCBkYXlfb2Zfd2VlayBjb2x1bW5zIGZvciB0aGUgbmV3IHRhYmxlCmRhdGFfdHdlZXRzX2RheXMgPC0gZGF0YV90d2VldHNfZGF5cyAlPiUKICBzZWxlY3QoaWQsIGNyZWF0ZWRfYXQsIGRheV9vZl93ZWVrKQoKcHJpbnQoZGF0YV90d2VldHNfZGF5cykKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBpZCBhbmQgZGF5X29mX3dlZWssIHRoZW4gY291bnRpbmcgdGhlIG51bWJlciBvZiB0d2VldHMKbnVtYmVyX3R3ZWV0c19kYXlzIDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgojIEdyb3VwaW5nIGJ5IGlkLCBkYXlfb2Zfd2VlayBhbmQgZGF5IGNyZWF0ZWQgYXQsIHRoZW4gY291bnRpbmcgdGhlIG51bWJlciBvZiB0d2VldHMKbnVtYmVyX3R3ZWV0c19wZXJfZGF5IDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgbXV0YXRlKGNyZWF0ZWRfZGF0ZSA9IGFzLkRhdGUoY3JlYXRlZF9hdCkpICU+JQogIGdyb3VwX2J5KGlkLCBkYXlfb2Zfd2VlaywgY3JlYXRlZF9kYXRlKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgojIEZpbmRpbmcgZm9yIGVhY2ggSEVJIHRoZSBhdmVyYWdlIGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5CmF2ZXJhZ2VfbnVtYmVyX3R3ZWV0c19wZXJfZGF5IDwtIG51bWJlcl90d2VldHNfcGVyX2RheSAlPiUKICBncm91cF9ieShpZCwgZGF5X29mX3dlZWspICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX2NvdW50ID0gbWVhbihjb3VudCkpCgpwcmludChudW1iZXJfdHdlZXRzX2RheXMpCmBgYAoKIyBIaWdoZXN0IGFuZCBsb3dlc3QgdHdlZXRzCgpgYGB7cn0KIyBGaW5kaW5nIHRoZSBIRUkgd2l0aCB0aGUgbG93ZXN0IGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5Cmxvd2VzdF9jb3VudCA8LSBudW1iZXJfdHdlZXRzX2RheXMgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWspICU+JQogIHNsaWNlX21pbihvcmRlcl9ieSA9IGNvdW50KSAlPiUKICBzZWxlY3QoZGF5X29mX3dlZWssIGlkLCBjb3VudCkKCiMgRmluZGluZyB0aGUgSEVJIHdpdGggdGhlIGhpZ2hlc3QgY291bnQgb2YgdHdlZXRzIHBlciBkYXkKaGlnaGVzdF9jb3VudCA8LSBudW1iZXJfdHdlZXRzX2RheXMgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWspICU+JQogIHNsaWNlX21heChvcmRlcl9ieSA9IGNvdW50KSAlPiUKICBzZWxlY3QoZGF5X29mX3dlZWssIGlkLCBjb3VudCkKCiMgQ29tYmluZSB0aGUgcmVzdWx0cwpoaWdoX2xvd19IRUkgPC0gYmluZF9yb3dzKGxvd2VzdF9jb3VudCwgaGlnaGVzdF9jb3VudCkgJT4lCiAgYXJyYW5nZShkYXlfb2Zfd2VlaykKCnByaW50KGhpZ2hfbG93X0hFSSkKYGBgCgojIFBsb3QgZm9yIHRoZSBsb3dlc3QgYW5kIGhpZ2hlc3QgY291bnQgb2YgdHdlZXRzIHBlciBkYXkgZm9yIGVhY2ggZGF5IG9mIHRoZSB3ZWVrCgpgYGB7cn0KZ2dwbG90KGhpZ2hfbG93X0hFSSwgYWVzKHggPSBkYXlfb2Zfd2VlaywgeSA9IGNvdW50LCBmaWxsID0gaWQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudCksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJMb3dlc3QgYW5kIEhpZ2hlc3QgQ291bnQgb2YgVHdlZXRzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJDb3VudCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSByYWluYm93KGxlbmd0aCh1bmlxdWUoaGlnaF9sb3dfSEVJJGlkKSkpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyBBdmVyYWdlIG9mIHR3ZWV0cwoKYGBge3J9CiMgRmluZGluZyB0aGUgSEVJIHdpdGggbG93ZXN0IGFuZCBoaWdoZXN0IGF2ZXJhZ2VkIGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5CmhpZ2hfbG93X2F2ZXJhZ2VfSEVJcyA8LSBhdmVyYWdlX251bWJlcl90d2VldHNfcGVyX2RheSAlPiUKICBncm91cF9ieShkYXlfb2Zfd2VlaykgJT4lCiAgZmlsdGVyKGF2ZXJhZ2VfY291bnQgPT0gbWF4KGF2ZXJhZ2VfY291bnQpIHwgYXZlcmFnZV9jb3VudCA9PSBtaW4oYXZlcmFnZV9jb3VudCkpICU+JQogIGFycmFuZ2UoZGF5X29mX3dlZWssIGlmZWxzZShhdmVyYWdlX2NvdW50ID09IG1pbihhdmVyYWdlX2NvdW50KSwgYXZlcmFnZV9jb3VudCwgLWF2ZXJhZ2VfY291bnQpKQoKcHJpbnQoaGlnaF9sb3dfYXZlcmFnZV9IRUlzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBhdmVyYWdlIGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIGRheSBvZiB0aGUgd2VlawoKYGBge3J9CmdncGxvdChoaWdoX2xvd19hdmVyYWdlX0hFSXMsIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBhdmVyYWdlX2NvdW50LCBmaWxsID0gaWQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChhdmVyYWdlX2NvdW50LCAyKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjcpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJIaWdoZXN0IGFuZCBMb3dlc3QgQXZlcmFnZSBDb3VudCBvZiBUd2VldHMgcGVyIERheSBmb3IgRWFjaCBEYXkgb2YgdGhlIFdlZWsiLAogICAgICAgeCA9ICJEYXkgb2YgdGhlIFdlZWsiLCB5ID0gIkF2ZXJhZ2UgQ291bnQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKGhpZ2hfbG93X0hFSSRpZCkpKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKIyBWaWV3cyBMaWtlcyBSZXR3ZWV0cyBhbmQgUmVwbGllcwoKYGBge3J9CiMgVGFibGUgY29udGFpbmluZyB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBsaWVzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIGVhY2ggSEVJCnR5cGVzX29mX3R3ZWV0cyA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCwgbWVkaWFfdHlwZSkgJT4lCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICB2aWV3cyA9IHN1bSh2aWV3X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBsaWtlcyA9IHN1bShmYXZvcml0ZV9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmV0d2VldHMgPSBzdW0ocmV0d2VldF9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGllcyA9IHN1bShyZXBseV9jb3VudCwgbmEucm0gPSBUUlVFKSkKICAgICAgICAgICAgICAgICAgICAgICAgCnByaW50KHR5cGVzX29mX3R3ZWV0cykgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCB2YWx1ZXMgb2Ygdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfdHdlZXRzX3N0YXRzIDwtIHR5cGVzX29mX3R3ZWV0cyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdzKSwKICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0obGlrZXMpLAogICAgICAgICAgICB0b3RhbF9yZXBsaWVzID0gc3VtKHJlcGxpZXMpKQoKcHJpbnQodG90YWxfdHdlZXRzX3N0YXRzKQpgYGAKCiMgRnVuY3Rpb24gZm9yIHBpZWNoYXJ0IGNyZWF0aW9uIGZvciB2aWV3cywgbGlrZXMgYW5kIHJlcGxpZXMgCgpgYGB7cn0KcGllX21ha2VyIDwtIGZ1bmN0aW9uKHRhcmdldF9pZCA9ICJkdWtlLmNzdiIpewogICMgRmlsdGVyaW5nIGRhdGEgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICBoZWlfZGF0YSA8LSB0eXBlc19vZl90d2VldHMgJT4lCiAgICBmaWx0ZXIoaWQgPT0gdGFyZ2V0X2lkKQogIAogICMgQ2FsY3VsYXRpbmcgdG90YWwgdmlld3MgZm9yIGVhY2ggbWVkaWEgdHlwZSBmb3IgdGhlIHNwZWNpZmljIEhFSQogIGhlaV9tZWRpYSA8LSBoZWlfZGF0YSAlPiUKICAgIGdyb3VwX2J5KG1lZGlhX3R5cGUpICU+JQogICAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdzKSwKICAgICAgICAgICAgICB0b3RhbF9saWtlcyA9IHN1bShsaWtlcyksCiAgICAgICAgICAgICAgdG90YWxfcmVwbGllcyA9IHN1bShyZXBsaWVzKSkKICAKICAjIENhbGN1bGF0aW5nIHRoZSBwZXJjZW50YWdlIG9mIHZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICBoZWlfbWVkaWEkcGVyY2VudGFnZV92aWV3IDwtIGhlaV9tZWRpYSR0b3RhbF92aWV3cyAvIHN1bShoZWlfbWVkaWEkdG90YWxfdmlld3MpICogMTAwCiAgaGVpX21lZGlhJHBlcmNlbnRhZ2VfbGlrZSA8LSBoZWlfbWVkaWEkdG90YWxfbGlrZXMgLyBzdW0oaGVpX21lZGlhJHRvdGFsX2xpa2VzKSAqIDEwMAogIGhlaV9tZWRpYSRwZXJjZW50YWdlX3JlcGx5IDwtIGhlaV9tZWRpYSR0b3RhbF9yZXBsaWVzIC8gc3VtKGhlaV9tZWRpYSR0b3RhbF9yZXBsaWVzKSAqIDEwMAogIAogICMgQ3JlYXRpbmcgdGhlIHBpZSBjaGFydCBmb3Igdmlld3MKICBoZWlfcGllX2NoYXJ0X3ZpZXdzIDwtIGdncGxvdChoZWlfbWVkaWEsIGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50YWdlX3ZpZXcsIGZpbGwgPSBtZWRpYV90eXBlKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKwogICAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUobWVkaWFfdHlwZSwgIlxuIiwgdG90YWxfdmlld3MsICIoIiwgcm91bmQocGVyY2VudGFnZV92aWV3LCAxKSwgIiUpIikpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3IgPSAiI0ZGRkZGRiIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIm5vX21lZGlhIiA9ICIjMjE5NkYzIiwgImFuaW1hdGVkX2dpZiIgPSAiI0U2N0UyMiIsICJwaG90byIgPSAiIzhFNDRBRCIsICJ2aWRlbyIgPSAiIzEzOEQ3NSIpKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIlZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgLSIsIHRhcmdldF9pZCkpCiAgCiAgIyBDcmVhdGluZyB0aGUgcGllIGNoYXJ0IGZvciBsaWtlcwogIGhlaV9waWVfY2hhcnRfbGlrZXMgPC0gZ2dwbG90KGhlaV9tZWRpYSwgYWVzKHggPSAiIiwgeSA9IHBlcmNlbnRhZ2VfbGlrZSwgZmlsbCA9IG1lZGlhX3R5cGUpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShtZWRpYV90eXBlLCAiXG4iLCB0b3RhbF9saWtlcywgIigiLCByb3VuZChwZXJjZW50YWdlX2xpa2UsIDEpLCAiJSkiKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICIjRkZGRkZGIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygibm9fbWVkaWEiID0gIiNFOTFFNjMiLCAiYW5pbWF0ZWRfZ2lmIiA9ICIjNEExNDhDIiwgInBob3RvIiA9ICIjOTBDQUY5IiwgInZpZGVvIiA9ICIjMDBCRkE1IikpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiTGlrZXMgZm9yIGVhY2ggbWVkaWEgdHlwZSAtIiwgdGFyZ2V0X2lkKSkKICAKICAjIENyZWF0aW5nIHRoZSBwaWUgY2hhcnQgZm9yIHJlcGxpZXMKICBoZWlfcGllX2NoYXJ0X3JlcGxpZXMgPC0gZ2dwbG90KGhlaV9tZWRpYSwgYWVzKHggPSAiIiwgeSA9IHBlcmNlbnRhZ2VfcmVwbHksIGZpbGwgPSBtZWRpYV90eXBlKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKwogICAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUobWVkaWFfdHlwZSwgIlxuIiwgdG90YWxfcmVwbGllcywgIigiLCByb3VuZChwZXJjZW50YWdlX3JlcGx5LCAxKSwgIiUpIikpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSwgY29sb3IgPSAiI0ZGRkZGRiIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIm5vX21lZGlhIiA9ICIjNjY2NjAwIiwgImFuaW1hdGVkX2dpZiIgPSAiIzk5Q0NDQyIsICJwaG90byIgPSAiIzk5NjZDQyIsICJ2aWRlbyIgPSAiIzMzMDAwMCIpKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIlJlcGxpZXMgZm9yIGVhY2ggbWVkaWEgdHlwZSAtIiwgdGFyZ2V0X2lkKSkKICAKICAjIFByaW50IHRoZSBwaWUgY2hhcnRzCiAgcHJpbnQoaGVpX3BpZV9jaGFydF92aWV3cykKICBwcmludChoZWlfcGllX2NoYXJ0X2xpa2VzKQogIHByaW50KGhlaV9waWVfY2hhcnRfcmVwbGllcykKfQpgYGAKCiMgUGxvdCBvZiBwaWVjaGFydHMgZm9yIGVhY2ggSEVJCgpgYGB7cn0KcGllX21ha2VyKCkKcGllX21ha2VyKCJlcGZsLmNzdiIpCnBpZV9tYWtlcigiZ29lLmNzdiIpCnBpZV9tYWtlcigiaGFydmFyZC5jc3YiKQpwaWVfbWFrZXIoImxlaWNlc3Rlci5jc3YiKQpwaWVfbWFrZXIoIm1hbmNoZXN0ZXIuY3N2IikKcGllX21ha2VyKCJtaXQuY3N2IikKcGllX21ha2VyKCJzYi5jc3YiKQpwaWVfbWFrZXIoInN0YW5mb3JkLmNzdiIpCnBpZV9tYWtlcigidHJpbml0eS5jc3YiKQpwaWVfbWFrZXIoInd2LmNzdiIpCnBpZV9tYWtlcigieWFsZS5jc3YiKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0aW9uIG9mIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX3R3ZWV0c190YWJsZSA8LSB0b3RhbF90d2VldHNfc3RhdHMgJT4lCiAgbXV0YXRlKGxpa2VfcmF0aW8gPSB0b3RhbF9saWtlcyAvIHRvdGFsX3ZpZXdzICogMTAwLAogICAgICAgICByZXBsaWVzX3JhdGlvID0gdG90YWxfcmVwbGllcyAvIHRvdGFsX3ZpZXdzICogMTAwKQoKIyBDcmVhdGlvbiBvZiBuZXcgdGFibGUgd2l0aCBlYWNoIEhFSSwgbGlrZV9yYXRpbywgYW5kIHJlcGxpZXNfcmF0aW8gCmhlaV90d2VldHNfcmF0aW9zIDwtIHJhdGlvc190d2VldHNfdGFibGUgJT4lCiAgc2VsZWN0KGlkLCBsaWtlX3JhdGlvLCByZXBsaWVzX3JhdGlvKSAlPiUKICBkaXN0aW5jdCgpCgpwcmludChoZWlfdHdlZXRzX3JhdGlvcykKYGBgCgojIFBsb3QgZm9yIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gZm9yIGVhY2ggSEVJCgpgYGB7cn0KZ2dwbG90KGhlaV90d2VldHNfcmF0aW9zLCBhZXMoeCA9IGlkKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gbGlrZV9yYXRpbywgZmlsbCA9ICJMaWtlIFJhdGlvIiksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX2JhcihhZXMoeSA9IHJlcGxpZXNfcmF0aW8sIGZpbGwgPSAiUmVwbHlzIFJhdGlvIiksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBsaWtlX3JhdGlvLCBsYWJlbCA9IHJvdW5kKGxpa2VfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiMwMDAwMDAiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gcmVwbGllc19yYXRpbywgbGFiZWwgPSByb3VuZChyZXBsaWVzX3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHNpemUgPSAzLCBjb2xvciA9ICIjRkZGRkZGIikgKwogIGxhYnModGl0bGUgPSAiTGlrZSBhbmQgUmVwbHlzIFJhdGlvcyBieSBIRUkiLAogICAgICAgeCA9ICJIRUkiLAogICAgICAgeSA9ICJSYXRpbyAoJSkiLAogICAgICAgZmlsbCA9ICJNZXRyaWMiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTGlrZSBSYXRpbyIgPSAiIzIxOTZGMyIsICJSZXBseXMgUmF0aW8iID0gIiNGNDQzMzYiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpCmBgYAoKYGBge3J9CiMgVGFibGUgd2l0aCBhdmVyYWdlcyBvZiB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBsaWVzCnR5cGVzX29mX3R3ZWV0c19wZXJfdHdlZXQgPC0gdHlwZXNfb2ZfdHdlZXRzICU+JQogICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieShpZCwgbWVkaWFfdHlwZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShhdmdfdmlld3MgPSBtZWFuKHZpZXdzIC8gY291bnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnX2xpa2VzID0gbWVhbihsaWtlcyAvIGNvdW50KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF2Z19yZXR3ZWV0cyA9IG1lYW4ocmV0d2VldHMgLyBjb3VudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfcmVwbGllcyA9IG1lYW4ocmVwbGllcyAvIGNvdW50KSkKCnByaW50KHR5cGVzX29mX3R3ZWV0c19wZXJfdHdlZXQpCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgSEVJIGFuZCBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSB2YWx1ZXMgb2Ygdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfYXZlcmFnZV9zdGF0cyA8LSB0eXBlc19vZl90d2VldHNfcGVyX3R3ZWV0ICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UoYXZnX3ZpZXdzID0gc3VtKGF2Z192aWV3cyksCiAgICAgICAgICAgIGF2Z19saWtlcyA9IHN1bShhdmdfbGlrZXMpLAogICAgICAgICAgICBhdmdfcmVwbGllcyA9IHN1bShhdmdfcmVwbGllcykpCgpwcmludCh0b3RhbF9hdmVyYWdlX3N0YXRzKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0aW9uIG9mIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX2F2ZXJhZ2VfdGFibGUgPC0gdG90YWxfYXZlcmFnZV9zdGF0cyAlPiUKICBtdXRhdGUobGlrZV9yYXRpbyA9IGF2Z19saWtlcyAvIGF2Z192aWV3cyAqIDEwMCwKICAgICAgICAgcmVwbGllc19yYXRpbyA9IGF2Z19yZXBsaWVzIC8gYXZnX3ZpZXdzICogMTAwKQoKIyBDcmVhdGlvbiBvZiBuZXcgdGFibGUgd2l0aCBlYWNoIEhFSSwgbGlrZV9yYXRpbywgYW5kIHJlcGxpZXNfcmF0aW8gCmhlaV9hdmVyYWdlX3JhdGlvcyA8LSByYXRpb3NfYXZlcmFnZV90YWJsZSAlPiUKICBzZWxlY3QoaWQsIGxpa2VfcmF0aW8sIHJlcGxpZXNfcmF0aW8pICU+JQogIGRpc3RpbmN0KCkKCnByaW50KGhlaV9hdmVyYWdlX3JhdGlvcykKYGBgCgojIFBsb3QgZm9yIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gZm9yIGVhY2ggSEVJCgpgYGB7cn0KZ2dwbG90KGhlaV9hdmVyYWdlX3JhdGlvcywgYWVzKHggPSBpZCkpICsKICBnZW9tX2JhcihhZXMoeSA9IGxpa2VfcmF0aW8sIGZpbGwgPSAiTGlrZSBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSByZXBsaWVzX3JhdGlvLCBmaWxsID0gIlJlcGxpZXMgUmF0aW8iKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMoeSA9IGxpa2VfcmF0aW8sIGxhYmVsID0gcm91bmQobGlrZV9yYXRpbywgMikpLCB2anVzdCA9IC0wLjUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBzaXplID0gMywgY29sb3IgPSAiIzAwMDAwMCIpICsKICBnZW9tX3RleHQoYWVzKHkgPSByZXBsaWVzX3JhdGlvLCBsYWJlbCA9IHJvdW5kKHJlcGxpZXNfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgbGFicyh0aXRsZSA9ICJMaWtlIGFuZCBSZXBsaWVzIFJhdGlvcyBieSBIRUkiLAogICAgICAgeCA9ICJIRUkiLAogICAgICAgeSA9ICJSYXRpbyAoJSkiLAogICAgICAgZmlsbCA9ICJNZXRyaWMiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiTGlrZSBSYXRpbyIgPSAiIzMzMDA2NiIsICJSZXBsaWVzIFJhdGlvIiA9ICIjRkY2NjY2IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdCA9IDEpKQpgYGAKCiMgSGFzaHRhZ3MKCmBgYHtyfQojIFRhYmxlIHdpdGggbnVtYmVyIG9mIHVuaXF1ZSBoYXNodGFncwp1bmlxdWVfaGFzaHRhZ3MgPC0gZGF0YV90d2VldHMgJT4lCiAgICAgICAgICAgICAgICBncm91cF9ieShpZCkgJT4lCiAgICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlX2hhc2h0YWdzID0gbGVuZ3RoKHVuaXF1ZShoYXNodGFncykpKQoKcHJpbnQodW5pcXVlX2hhc2h0YWdzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGNvdW50IG9mIHVuaXF1ZSBoYXNodGFncyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsCiAgICAgICAgbmFtZXMuYXJnID0gdW5pcXVlX2hhc2h0YWdzJGlkLAogICAgICAgIG1haW4gPSAiVW5pcXVlIEhhc2h0YWdzIGZvciBFYWNoIEhFSSIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQ291bnQgb2YgVW5pcXVlIEhhc2h0YWdzIiwKICAgICAgICB5bGltID0gYygwLCBtYXgodW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncykgKyA1MCksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2w9ICIjMTZBMDg1IikKCnRleHQoeCA9IGJhcnBsb3QodW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gdW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywKICAgICBsYWJlbHMgPSByb3VuZCh1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgSGVhdG1hcHMKCmBgYHtyfQojIENyZWF0ZSBjb2x1bW4gaG91ciBmcm9tIGNyZWF0ZWRfYXQKZGF0YV90d2VldHNfZGF5cyRjcmVhdGVkX2hvdXIgPC0gYXMubnVtZXJpYyhmb3JtYXQoZGF0YV90d2VldHNfZGF5cyRjcmVhdGVkX2F0LCAiJUgiKSkKYGBgCgojIEZ1bmN0aW9uIHRvIHBsb3QgaGVhdG1hcCBmb3IgdmFyaW91cyBIRUlzCgpgYGB7cn0KaGVhdG1hcF9tYWtlciA8LSBmdW5jdGlvbih0YXJnZXRfaWQgPSAiZHVrZS5jc3YiKXsKICAjIEZpbHRlcmluZyBkYXRhIGZvciB0aGUgc3BlY2lmaWMgSEVJCiAgdGFyZ2V0X2RhdGEgPC0gZGF0YV90d2VldHNfZGF5cyAlPiUKICAgIGZpbHRlcihpZCA9PSB0YXJnZXRfaWQpCiAgCiAgIyBHcm91cGluZyBieSBkYXkgb2YgdGhlIHdlZWsgYW5kIGhvdXIsIGFuZCBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHR3ZWV0cwogIHR3ZWV0X2NvdW50cyA8LSB0YXJnZXRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGRheV9vZl93ZWVrLCBjcmVhdGVkX2hvdXIpICU+JQogICAgc3VtbWFyaXNlKG51bV90d2VldHMgPSBuKCkpCiAgCiAgIyBQbG90dGluZyBoZWF0bWFwCiAgZ2dwbG90KHR3ZWV0X2NvdW50cywgYWVzKHggPSBkYXlfb2Zfd2VlaywgeSA9IGNyZWF0ZWRfaG91ciwgZmlsbCA9IG51bV90d2VldHMpKSArCiAgICBnZW9tX3RpbGUoKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiYmx1ZSIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiVHdlZXQgSGVhdG1hcCBmb3IiLCB0YXJnZXRfaWQpLAogICAgICAgICB4ID0gIkRheSBvZiB0aGUgd2VlayIsCiAgICAgICAgIHkgPSAiSG91ciBvZiB0aGUgZGF5IikKfQoKaGVhdG1hcF9tYWtlcigpCmhlYXRtYXBfbWFrZXIoImVwZmwuY3N2IikKaGVhdG1hcF9tYWtlcigiZ29lLmNzdiIpCmhlYXRtYXBfbWFrZXIoImhhcnZhcmQuY3N2IikKaGVhdG1hcF9tYWtlcigibGVpY2VzdGVyLmNzdiIpCmhlYXRtYXBfbWFrZXIoIm1hbmNoZXN0ZXIuY3N2IikKaGVhdG1hcF9tYWtlcigibWl0LmNzdiIpCmhlYXRtYXBfbWFrZXIoInNiLmNzdiIpCmhlYXRtYXBfbWFrZXIoInN0YW5mb3JkLmNzdiIpCmhlYXRtYXBfbWFrZXIoInRyaW5pdHkuY3N2IikKaGVhdG1hcF9tYWtlcigid3YuY3N2IikKaGVhdG1hcF9tYWtlcigieWFsZS5jc3YiKQpgYGAKCiMgVGV4dAoKYGBge3J9CmRhdGFfdHdlZXRzX2NvbnRlbnQgPC0gZGF0YV90d2VldHMgJT4lCiAgICAgICAgICAgIHNlbGVjdChpZCwgdGV4dCkKCiMgQ291bnRpbmcgbnVtYmVyIG9mIHdvcmRzCmRhdGFfdHdlZXRzX2NvbnRlbnQgPC0gZGF0YV90d2VldHNfY29udGVudCAlPiUKICBtdXRhdGUobnVtX3dvcmRzID0gbGVuZ3RocyhzdHJzcGxpdCh0ZXh0LCAiXFxzKyIpKSkKCnByaW50KGRhdGFfdHdlZXRzX2NvbnRlbnQpCgojIEdyb3VwaW5nIGJ5IEhFSSBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2UsIG1pbmltdW0sIGFuZCBtYXhpbXVtIHZhbHVlcyBvZiBudW1iZXIgb2Ygd29yZHMKZGF0YV90d2VldHNfY29udGVudF9tZXRyaWNzIDwtIGRhdGFfdHdlZXRzX2NvbnRlbnQgJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZShhdmVyYWdlX251bV93b3JkcyA9IG1lYW4obnVtX3dvcmRzKSwKICAgICAgICAgICAgbWluX251bV93b3JkcyA9IG1pbihudW1fd29yZHMpLAogICAgICAgICAgICBtYXhfbnVtX3dvcmRzID0gbWF4KG51bV93b3JkcykpCnByaW50KGRhdGFfdHdlZXRzX2NvbnRlbnRfbWV0cmljcykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlLCBtYXhpbXVtIGFuZCBtaW5pbXVtIHZhbHVlcyBvZiB3b3JkcyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpnZ3Bsb3QoZGF0YV90d2VldHNfY29udGVudF9tZXRyaWNzLCBhZXMoeCA9IGlkLCB5ID0gYXZlcmFnZV9udW1fd29yZHMpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAiQXZlcmFnZSIpKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1pbl9udW1fd29yZHMsIHltYXggPSBtYXhfbnVtX3dvcmRzLCBjb2xvciA9ICJSYW5nZSIpLCB3aWR0aCA9IDAuMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJBdmVyYWdlIiA9ICIjMTk3NkQyIiwgIlJhbmdlIiA9ICIjRUY1MzUwIikpICsKICBsYWJzKHRpdGxlID0gIldvcmQgQ291bnQgU3VtbWFyeSBieSBIRUkiLAogICAgICAgeCA9ICJIRUkiLAogICAgICAgeSA9ICJOdW1iZXIgb2YgV29yZHMiLAogICAgICAgY29sb3IgPSAiTWV0cmljIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpCmBgYAoKIyBOb3cgcmVwbGllcyAKCmBgYHtyfQpkYXRhX3JlcGxpZXMgPC0gZGF0YVtkYXRhJHR5cGUgPT0gIlJlcGx5IiwgXQoKZGF0YV9yZXBsaWVzCmBgYAoKIyBJbnRlcmFjdGlvbnMgdG8gcmVwbGllcwoKYGBge3J9CiMgVGFibGUgY29udGFpbmluZyB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBsaWVzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIGVhY2ggSEVJCnR5cGVzX29mX3JlcGxpZXMgPC0gZGF0YV9yZXBsaWVzICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkLCBtZWRpYV90eXBlKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdzID0gc3VtKHZpZXdfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpa2VzID0gc3VtKGZhdm9yaXRlX2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICByZXR3ZWV0cyA9IHN1bShyZXR3ZWV0X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICByZXBsaWVzID0gc3VtKHJlcGx5X2NvdW50LCBuYS5ybSA9IFRSVUUpKQogICAgICAgICAgICAgICAgICAgICAgICAKcHJpbnQodHlwZXNfb2ZfcmVwbGllcykgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCB2YWx1ZXMgb2Ygdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfcmVwbGllc19zdGF0cyA8LSB0eXBlc19vZl9yZXBsaWVzICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UodG90YWxfdmlld3MgPSBzdW0odmlld3MpLAogICAgICAgICAgICB0b3RhbF9saWtlcyA9IHN1bShsaWtlcyksCiAgICAgICAgICAgIHRvdGFsX3JlcGxpZXMgPSBzdW0ocmVwbGllcykpCgpwcmludCh0b3RhbF9yZXBsaWVzX3N0YXRzKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0aW9uIG9mIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX3JlcGxpZXNfdGFibGUgPC0gdG90YWxfcmVwbGllc19zdGF0cyAlPiUKICBtdXRhdGUobGlrZV9yYXRpbyA9IHRvdGFsX2xpa2VzIC8gdG90YWxfdmlld3MgKiAxMDAsCiAgICAgICAgIHJlcGxpZXNfcmF0aW8gPSB0b3RhbF9yZXBsaWVzIC8gdG90YWxfdmlld3MgKiAxMDApCgojIENyZWF0aW9uIG9mIG5ldyB0YWJsZSB3aXRoIGVhY2ggSEVJLCBsaWtlX3JhdGlvLCBhbmQgcmVwbGllc19yYXRpbyAKaGVpX3JlcGxpZXNfcmF0aW9zIDwtIHJhdGlvc19yZXBsaWVzX3RhYmxlICU+JQogIHNlbGVjdChpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX3JlcGxpZXNfcmF0aW9zKQpgYGAKCiMgQ2x1c3RlcnMKCmBgYHtyfQojIENyZWF0aW5nIHRhYmxlIGZvciBjbHVzdGVyIGFsZ29yaXRobXMKCiMgSm9pbmluZyBhdHRyaWJ1dGUgY291bnQgKG51bWJlciBvZiB0d2VldHMpIGFuZCB1bmlxdWVfaGFzaHRhZ3MgKG51bWJlciBvZiB1bmlxdWUgaGFzaHRhZ3MpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShzZWxlY3QodW5pcXVlX2hhc2h0YWdzLCBpZCwgdW5pcXVlX2hhc2h0YWdzKSwgc2VsZWN0KG51bWJlcl90d2VldHMsIGlkLCBjb3VudCksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfcGVyX2RheXMgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciBkYXkpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QodHdlZXRzX3Blcl9kYXksIGlkLCBhdmdfdHdlZXRzX3Blcl9kYXlzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19wZXJfd2Vla3MgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciB3ZWVrKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHR3ZWV0c19wZXJfd2VlaywgaWQsIGF2Z190d2VldHNfcGVyX3dlZWtzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lIChhdmVyYWdlIG9mIHR3ZWV0cyBkdXJpbmcgYWNhZGVtaWMgdGltZSkgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChkYXRhX3R3ZWV0c19hY2FkZW1pYywgaWQsIGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSAoYXZlcmFnZSBvZiB0d2VldHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZGF0YV90d2VldHNfdmFjYXRpb25zLCBpZCwgYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgdG90YWxfdmlld3MgKHRvdGFsIG51bWJlciBvZiB2aWV3cyksIHRvdGFsX2xpa2VzICh0b3RhbCBudW1iZXIgb2YgbGlrZXMpIGFuZCB0b3RhbF9yZXBsaWVzICh0b3RhbCBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh0b3RhbF90d2VldHNfc3RhdHMsIGlkLCB0b3RhbF92aWV3cywgdG90YWxfbGlrZXMsIHRvdGFsX3JlcGxpZXMpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBSZW5hbWluZyBhdHRyaWJ1dGUgbGlrZV9yYXRpbyB0byB0b3RhbF9saWtlX3JhdGlvIGFuZCByZXBsaWVzX3JhdGlvIHRvIHRvdGFsX3JlcGxpZXNfcmF0aW8KY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoaGVpX3R3ZWV0c19yYXRpb3MsIGlkLCBsaWtlX3JhdGlvLCByZXBsaWVzX3JhdGlvKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKY2x1c3Rlcl90YWJsZSA8LSBjbHVzdGVyX3RhYmxlICU+JQogIHJlbmFtZSh0b3RhbF9saWtlX3JhdGlvID0gbGlrZV9yYXRpbywgCiAgICAgICAgIHRvdGFsX3JlcGxpZXNfcmF0aW8gPSByZXBsaWVzX3JhdGlvKQoKIyBKb2luaW5nIGF0dHJpYnV0ZSBhdmdfdmlld3MgKGF2ZXJhZ2UgbnVtYmVyIG9mIHZpZXdzKSwgYXZnX2xpa2VzIChhdmVyYWdlIG51bWJlciBvZiBsaWtlcykgYW5kIGF2Z19yZXBsaWVzIChhdmVyYWdlIG51bWJlciBvZiByZXBsaWVzKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHRvdGFsX2F2ZXJhZ2Vfc3RhdHMsIGlkLCBhdmdfdmlld3MsIGF2Z19saWtlcywgYXZnX3JlcGxpZXMpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBSZW5hbWluZyBhdHRyaWJ1dGUgbGlrZV9yYXRpbyB0byBhdmdfbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyB0byBhdmdfcmVwbGllc19yYXRpbwpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChoZWlfYXZlcmFnZV9yYXRpb3MsIGlkLCBsaWtlX3JhdGlvLCByZXBsaWVzX3JhdGlvKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKY2x1c3Rlcl90YWJsZSA8LSBjbHVzdGVyX3RhYmxlICU+JQogIHJlbmFtZShhdmdfbGlrZV9yYXRpbyA9IGxpa2VfcmF0aW8sIAogICAgICAgICBhdmdfcmVwbGllc19yYXRpbyA9IHJlcGxpZXNfcmF0aW8pCgojIGxpa2VzIHZpZXdzIHJlcGx5cyByZXR3ZWV0cyBkb250IG1hdHRlciAhPz8hPyE/IT8hPyE/CiMgUGFyYSBhY3JlY2VudGFyCiMgbW9zdCBwb3B1bGFyIGhvdXIKIyBtb3N0IHBvcHVsYXIgZGF5CiMgdXNhZ2Ugb2YgaGFzaHRhZ3MKIyB1c2FnZSBvZiB1cmxzCiMgdXNhZ2UgbWVkaWFfdHlwZQojIHZlciBzZSBlc3TDo28gY29ycmVsYWNpb25hZGFzCgpwcmludChjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRnVuY3Rpb24gZm9yIGNsdXN0ZXIgbWV0aG9kCgpgYGB7cn0KY2x1c3Rlcl9tYWtlciA8LSBmdW5jdGlvbihudW1fY2x1c3RlcnMgPSAzLCB0YWJsZSl7CiAgCiAgIyBFeGNsdWRpbmcgaWQgY29sdW1uIGZvciBjbHVzdGVyaW5nCiAgY2x1c3Rlcl9kYXRhIDwtIHNlbGVjdCh0YWJsZSwgLWlkKQogIAogICMgU2NhbGluZyB0aGUgZGF0YSBmb3Iga21lYW5zIG1ldGhvZAogIHNjYWxlZF9kYXRhIDwtIHNjYWxlKGNsdXN0ZXJfZGF0YSkKICAKICBrbWVhbnNfcmVzdWx0IDwtIGttZWFucyhzY2FsZWRfZGF0YSwgY2VudGVycyA9IG51bV9jbHVzdGVycywgbnN0YXJ0ID0gMTApCiAgCiAgcHJpbnQoa21lYW5zX3Jlc3VsdCRjZW50ZXJzKQogIHByaW50KGttZWFuc19yZXN1bHQkY2x1c3RlcikKICAKICByZXR1cm4oa21lYW5zX3Jlc3VsdCkKfQpgYGAKCiMgRnVuY3Rpb24gdG8gYWRkIGlkcyB0byBiZXR0ZXIgdmlzdWFsaXplIHJlc3VsdHMKCmBgYHtyfQpjbHVzdGVyX2lkX21ha2VyIDwtIGZ1bmN0aW9uKGttZWFuc19yZXN1bHQsIHRhYmxlKXsKICAjIE1lcmdpbmcgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YQogIGNsdXN0ZXJfYXNzaWdubWVudHMgPC0gZGF0YS5mcmFtZShpZCA9IHRhYmxlJGlkLCBjbHVzdGVyID0ga21lYW5zX3Jlc3VsdCRjbHVzdGVyKQoKICBwcmludChjbHVzdGVyX2Fzc2lnbm1lbnRzKQogIHBsb3Qoa21lYW5zX3Jlc3VsdCRjbHVzdGVyKQp9CmBgYAoKIyBUaHJlZSBjbHVzdGVycyB3aXRoIHNlZWQgMTIzCgpgYGB7cn0KY2x1c3Rlcl8xMjNfMyA8LSBjbHVzdGVyX21ha2VyKHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM18zLCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBTZXZlbiBjbHVzdGVycyB3aXRoIHNlZWQgMTIzCgpgYGB7cn0KY2x1c3Rlcl8xMjNfNiA8LSBjbHVzdGVyX21ha2VyKG51bV9jbHVzdGVycyA9IDcsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM182LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBGaXZlIGNsdXN0ZXJzIHdpdGggc2VlZCAxMjMKCmBgYHtyfQpjbHVzdGVyXzEyM182IDwtIGNsdXN0ZXJfbWFrZXIobnVtX2NsdXN0ZXJzID0gNSwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpjbHVzdGVyX2lkX21ha2VyKGNsdXN0ZXJfMTIzXzYsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKYGBgCgojIEZvdXIgY2x1c3RlcnMgd2l0aCBzZWVkIDQ4NTUKCmBgYHtyfQpjbHVzdGVyXzEyM18zIDwtIGNsdXN0ZXJfbWFrZXIoc2VlZCA9IDQ4NTUsIG51bV9jbHVzdGVycyA9IDQsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM18zLCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBTaXggY2x1c3RlcnMgd2l0aCBzZWVkIDQ4NTUKCmBgYHtyfQpjbHVzdGVyXzEyM182IDwtIGNsdXN0ZXJfbWFrZXIoc2VlZCA9IDQ4NTUsIG51bV9jbHVzdGVycyA9IDYsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM182LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoK